# coding=utf-8
'''
Created on Jun 15, 2009

@license: License: LGPL
@copyright: (c) 2009, dogsing.cn
@author: ant-man
'''

from mocObject import MocObject
from copy import deepcopy
from cn.pigersing.moc.utils.langUtils import StaticNum


class MocInstance(object):
    '''
    Represent the instance of mocObjects
    '''

    def __init__(self, id, meta_object, value=None):
        """
        TODO:
        """
        assert isinstance(meta_object, (MocObject, type(None)))
        self.meta_object = meta_object
        self.value = value
        self._id = id
        self.__refrence_count = 0
        # this is a local field key, these fields are added at runtime dynamically
        self.__field = {}
        self.__parent = self

    def __parent(): #@NoSelf

        def fget(self):
            return self.__parent

        def fset(self, value):
            assert isinstance(value, MocInstance)
            self.__parent = value

        return locals()

    parent = property(**__parent())

    @property
    def type(self):
        return self.meta_object.TYPE

    @property
    def id(self):
        return self._id

    def clone(self):
        """
        @return: the deep copy of current instance
        """
        # TODO: should this be removed?
        return new_instance(self.meta_object, deepcopy(self.value))

    def refer(self):
        """
        """
        self.__refrence_count += 1
        return self

    def derefer(self):
        """
        """
        self.__refrence_count -= 1
        self.clean()

    def clean(self):
        if self.__refrence_count <= 0 :
            del inst_list[self.id]

    def assign(self, rhs):
        """
        Perform the assignment of rhs to take place the current instance, when the
        instance is used as temporary this might happen
        
        @param rhs: the source that will replace this instance
        """
        assert isinstance(rhs, MocInstance)
        self.meta_object = rhs.meta_object
        self.value = rhs.value

    def add_field(self, name, inst):
        assert isinstance(name, str)
        assert isinstance(inst, MocInstance)
        self.__field[name] = inst.id

    def get_field(self, name):
        filed_id = self.__field.get(name, None)
        if not filed_id :
            # if can not find in local, try to find in its object
            filed_id = self.meta_object.get_field(name)
        if filed_id :
            return inst_list[filed_id]
        # dose not have a such name field
        return None

    def new_inst(self, value):
        return self.meta_object.new_inst(value)

    def binary_op(self, op, rhs):
        return self.meta_object.binary_op(op, self, rhs)

    def unary_op(self, op):
        return self.meta_object.unary_op(op, self)

    def type_equals(self, rhs):
        """
        """
        if not self.meta_object :return False
        return self.meta_object.equals(rhs)

    def pyval(self):
        """
        """
        if not self.meta_object : return None
        return self.meta_object.pyval(self)

    def __getattr__(self, name):
        if name.startswith("as") :
            return lambda :getattr(self.meta_object, name)(self)
        elif hasattr(self.meta_object, name) :
            return lambda * vargs:getattr(self.meta_object, name)(self, *vargs)
        raise AttributeError()

    def __str__(self):
        return  str(self.value)


class MocTempInst(MocInstance):

    reference_id = -1

    def refer(self):
        """
        """
        if self.reference_id > 0 :
            return inst_list[self.reference_id].refer()

    def derefer(self):
        """
        """
        if self.reference_id > 0 :
            return inst_list[self.reference_id].derefer()

    def clone(self):
        """
        @return: the deep copy of current instance
        """
        return inst_list[self.reference_id].clone()

    def assign(self, rhs):
        """
        Perform the assignment of rhs to take place the current instance, when the
        instance is used as temporary this might happen
        
        @param rhs: the source that will replace this instance
        """
        assert isinstance(rhs, MocInstance)
        self.reference_id = rhs.id
        self.meta_object = rhs.meta_object
        self.value = rhs.value

    @property
    def id(self):
        return self.reference_id

    def add_field(self, name, inst):
        if self.reference_id > -1 :
            return inst_list[self.id].add_field(name, inst)

    def get_field(self, name):
        if self.reference_id > -1 :
            return inst_list[self.id].get_field(name)


    def __parent(): #@NoSelf

        def fget(self):
            if self.reference_id > -1 :
                return inst_list[self.id].parent

        def fset(self, value):
            if self.reference_id > -1 :
                inst_list[self.id].parent = value

        return locals()

    parent = property(**__parent())

    def __str__(self):
        return "temp%d" % self._id


__inst_id = StaticNum(0)


inst_list = {}

def new_instance(meta_object, value=None):
    inst = MocInstance(__inst_id(), meta_object, value)
    inst_list[inst.id] = inst
    return inst


__temp = [
        MocTempInst(0, None),
        MocTempInst(1, None),
        MocTempInst(2, None),
        MocTempInst(3, None),
        MocTempInst(4, None),
]

__tempcout = 0

def newtemp():
    global __tempcout
    ret = __temp[__tempcout]
    __tempcout = (__tempcout + 1) % 5
    return ret
